/*
...
   SD_SPICON1 = SD_SPICON1_CFG;
   SD_SPISTAT = SD_SPISTAT_CFG;
...
#if defined(USE_SD)
   DelayMs(250);
   if (SD_PRESENT == 0)   // sd card presente
   {
	   SDInit();                // Initialize file system
	   DelayMs(100);
	   if (SDInit() == TRUE)   // retry Initialize file system
	   {
		   putrsUART("SD ready!\r\n");
	   }
	   else
		   putrsUART("SD init ERR!\r\n");
   }
   else
	  putrsUART("SD not inserted\r\n");
#else
   putrsUART("SD not used!\r\n");
#endif
...
...
...*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include "globals.h"
#include "spi.h"
#include "sd.h"
#include "math.h"
#include <util/delay.h>

/*********************************************************************
*       SD memory card driver - mia versione modificata per tcpip
********************************************************************/
#define HIGH(x)         (x << 8)
#define LOW(x)          (x & 0xFF)

#define SECTOR_SIZE	512


uint16_t	nBlkLen = SECTOR_SIZE;


//#pragma udata sd_buffer
//char sd_buffer[SDBUFF_SIZE] __attribute__((space(dma)));
//#pragma udata

//static uint32_t currentSector;
//static WORD  currentOffset;
//extern unsignedint32_tSD_SPEED;
/*
--------------------------------------------------------------------------------------
routine di interfaccia logica spi  - scrive e legge un byte                          -
--------------------------------------------------------------------------------------
*/
#define spi(x) spiTransferByte(x)
void DelayMs(uint16_t ms)
{
	uint16_t i;
	for (i=0; i<ms;i++)
	{
		wdt_reset();
		_delay_ms(1);
	}
}
/*
--------------------------------------------------------------------------------------
routine di interfaccia logica SD - invia un comando CMD                              -
--------------------------------------------------------------------------------------
*/
void SDCmd(uint8_t cmd, unsigned long param)
{
	   spi(0xFF);                                                    // extra clock
	   spi(cmd);                                                     // command
	   spi(param >> 24);
	   spi(param >> 16);
	   spi(param >> 8);
	   spi(param);
	   spi(cmd == (CMD0) ? 0x95 : 0xFF);       // CRC
}
/*
--------------------------------------------------------------------------------------
legge SD attraverso SPI fino a ricevere un carattere diverso da 0xFF o timeout       -
--------------------------------------------------------------------------------------
*/
uint8_t SDWait(void)
{
	   uint16_t count = 0xFF;
	   uint8_t response;
	   while((response=spi(0xFF)) == (uint8_t)0xFF && --count > 0);
	   return response;
}

uint8_t SDRead(uint8_t *buffer, uint8_t length)
{
	uint8_t *ptr = buffer;// + length;
	do
	{
		*ptr++ = spi(0xFF);
	} while (--length);

	return TRUE;
}

uint32_t SDGetSize(void)
{
	cbi(MMC_CS_PORT,MMC_CS_PIN);

	SDCmd(SEND_CSD,0);

	if (SDWait() == 0x00
	&&	SDWait() != 0xFF)
	{
		_typSdCSD	CSD;
		uint32_t	nSize =0;
		uint32_t	nMult = 0;
		uint8_t		i;
		SDRead(CSD.raw.bytes, sizeof(_typSdCSD));

		spi(0xFF);
		spi(0xFF);
		spi(0xFF);
		spi(0xFF);
		
		nSize = ((uint32_t)CSD.raw.bytes[6] & 0x03) << 10;
		nSize |= (uint32_t)CSD.raw.bytes[7] << 2;
		nSize |= CSD.raw.bytes[8] >> 6;
		nSize += 1;

		nMult = (CSD.raw.bytes[9] & 0x03) << 1;
		nMult |= (CSD.raw.bytes[10]) >> 7;
		nMult += 2;

		i = nMult;
		nMult = 2;
		while (--i)
		{
			nMult *= 2;
		};

		nSize = nSize * nMult;

		return nSize;
	}

	sbi(MMC_CS_PORT,MMC_CS_PIN);

	return 0;
}

uint8_t SDGetCID(_typSdCID *data)
{
	uint8_t	bStatus = FALSE;

	cbi(MMC_CS_PORT,MMC_CS_PIN);

	SDCmd(SEND_CID,0);

	if (SDWait() == 0x00
	&&	SDWait() != 0xFF)
	{
		SDRead((uint8_t*)data, sizeof(_typSdCID));
	
		spi(0xFF);
		spi(0xFF);
		spi(0xFF);
		spi(0xFF);

		spi(0xFF);
		spi(0xFF);

		bStatus = TRUE;
	}

	sbi(MMC_CS_PORT,MMC_CS_PIN);

	return bStatus;
}

uint8_t SDWriteFromSector(uint32_t sector, uint8_t *sdbuff, uint16_t length, uint8_t *sdbuff2, uint16_t length2)
{
	uint32_t i;
	uint8_t delay;
	cbi(MMC_CS_PORT,MMC_CS_PIN);

	if (nBlkLen != SECTOR_SIZE )
	{
		SDCmd(SET_BLK_LEN, SECTOR_SIZE);
		if (SDWait() != 0x00)
		{
			sbi(MMC_CS_PORT,MMC_CS_PIN);
			return FALSE;                           // no response nor data token
		}
		nBlkLen = SECTOR_SIZE;
	}

	SDCmd(CMD24, (sector * SECTOR_SIZE));
	if(SDWait()!=0x00)
	{
		   sbi(MMC_CS_PORT,MMC_CS_PIN);
		   return FALSE;              // no response nor data token
	}
	spi(0xFF);
	spi(0xFF);
	spi(0xFE);
	for (i = 0; i < SECTOR_SIZE; i++)
	{
		if (i < length)
		{
			 spi(*sdbuff);       // write byte
			 sdbuff ++;          // increment pointer
		}
		else if (i < (length + length2))
		{
			spi(*sdbuff2);       // write byte
			sdbuff2 ++;          // increment pointer
		}
		else
		{
			 spi(0xFF);
		}
	}
	spi(0xFF);
	spi(0xFF);                         // flush CRC
	delay = 255;
	while ((spi(0xFF) & 0x1F) != 0x05) {
		if (!delay--) {
			sbi(MMC_CS_PORT,MMC_CS_PIN);
			return FALSE;
		}
			DelayMs(1);
	}
	while (spi(0xFF) == 0x00);
	sbi(MMC_CS_PORT,MMC_CS_PIN);
	return  TRUE;
} 

/*
--------------------------------------------------------------------------------------
indirizza un settore SD e lo legge indirizzando i dati ove richiesto                 -
--------------------------------------------------------------------------------------
*/
uint8_t SDReadFromSector(uint32_t sector, uint8_t *sdbuff, uint32_t length, uint16_t offset)
{
	int32_t  i;
	cbi(MMC_CS_PORT,MMC_CS_PIN);

	if (nBlkLen != length )
	{
		SDCmd(SET_BLK_LEN, length);
		if (SDWait() != 0x00)
		{
			sbi(MMC_CS_PORT,MMC_CS_PIN);
			return FALSE;                           // no response nor data token
		}
		nBlkLen = length;
	}

	SDCmd(CMD17, (sector * SECTOR_SIZE)+offset);
	if(SDWait()!=0x00)
	{
		sbi(MMC_CS_PORT,MMC_CS_PIN);
		return FALSE;                           // no response nor data token
	}
	if(SDWait()!=0xFE)
	{
		sbi(MMC_CS_PORT,MMC_CS_PIN);
		return FALSE;                           // no response nor data token
	}
	
	for (i = 0; i < length; i++)
	{
		*sdbuff = spi(0xFF);      // read block
		 sdbuff ++;               // increment pointer
	}

	/*for (i = 0; i < SECTOR_SIZE; i++)
	{
		if (i < length)
		{
			*sdbuff = spi(0xFF);      // read block
			 sdbuff ++;               // increment pointer
		}
		else
		{
			spi(0xFF);               // read block
		}
	}*/

	spi(0xFF);
	spi(0xFF);                                              // flush CRC
	sbi(MMC_CS_PORT,MMC_CS_PIN);
	return  TRUE;
}

/*
--------------------------------------------------------------------------------------
routine di inizializzazione fisica SD tramite interfaccia SPI                        -
--------------------------------------------------------------------------------------
*/
uint8_t SDInit(void)
{
		int32_t i;

		spiInit(0);

		// release chip select
		sbi(MMC_CS_DDR, MMC_CS_PIN);
		sbi(MMC_CS_PORT,MMC_CS_PIN);

		for(i=0; i < 80; i++)
			   spi(0xFF);                 // powerup extra clocks

		cbi(MMC_CS_PORT,MMC_CS_PIN);

		i= 40;
		do {
			SDCmd(CMD0, 0);                    // send Reset Command
		} while (SDWait() != 0x01 && --i);

		if (!i)
		{
			sbi(MMC_CS_PORT,MMC_CS_PIN);
			return FALSE;
		}

		i = 50;
		do {
			DelayMs(10);
			SDCmd(CMD55, 0);
			SDWait();
			SDCmd(ACMD41,0);
			if (i-- < 0)
			{
				sbi(MMC_CS_PORT,MMC_CS_PIN);
				return FALSE;
			}
		} while (SDWait() != 0x00 );       // wait until SD is not in Idle Mode

		spiInit(1);

		// Set the block lengths
		SDCmd(SET_BLK_LEN,SECTOR_SIZE);
		SDWait();
		nBlkLen = SECTOR_SIZE;

		sbi(MMC_CS_PORT,MMC_CS_PIN);
		return  TRUE;
} 
